<?php
/**
 * @author Jumper Swordwave
 * date: 2021/04/19
 */
declare(strict_types=1);
namespace App\Game\Service;

use Psr\Container\ContainerInterface;
use Hyperf\Utils\Collection;
use App\Game\Core\GameStatus;
use App\Game\Model\User;

class UserService extends BaseService
{
    /**
     * 用户信息缓存key
     */
    private $user_info_key;

    /**
     * 用户集合
     */
    private $user_list_key;
    
    public function __construct(ContainerInterface $container)
    {
        parent::__construct($container);
        $this->user_info_key = config('game.key.user_info', 'user:info:%d');
        $this->user_list_key = config('game.key.user_list', 'user:list');
    }

    /**
     * 查找用户信息
     * @param int       $fd
     * @param ?array    $udata
     * @return ?User 
     */
    public function findUser(int $fd, ?array $udata=null) : ?User
    {
        $id = $fd;
        if ($udata && isset($udata['id']) && $udata['id'] > 0) {
            $id = $udata['id'];
        }

        return $this->get(sprintf($this->user_info_key, $id));
    }

    /**
     * 根据id查找用户信息
     * @param int   $id
     * @return ?User
     */
    public function findUserById(int $id) : ?User
    {
        if ($id <= 0) return null;
        return $this->get(sprintf($this->user_info_key, $id));
    }

    /**
     * 查找一组用户
     * @param array $ids    用户id数组
     * @return Collection
     */
    public function findUsers(array $ids) : Collection 
    {
        $users = [];
        foreach($ids as $id) {
            $user = $this->findUserById(intval($id));
            if ($user) {
                $users[] = $user;
            }
        }
        return new Collection($users);
    }

    /**
     * 保存用户
     * @param User  $user
     */
    public function saveUser(User $user) : void
    {
        $this->save(sprintf($this->user_info_key, $user->id), $user);
    }

    /**
     * 保存全部用户
     * @param array|Collection  $users
     */
    public function saveUsers($users)
    {
        foreach($users as $user) {
            $this->saveUser($user);
        }
    }
    
    /**
     * 新建用户
     * @param int   $fd
     * @param array $data
     * @return User
     */
    public function newUser(int $fd, array $data) : User
    {
        $user = new User($data);
        $user->fd = $fd;
        $user->id = $fd;
        $user->status = GameStatus::USER_WAITING;
        $user->updated_at = time();
        $this->saveUser($user);
        $this->redis->sadd($this->user_list_key, $user->id);
        return $user;
    }

    /**
     * 更新用户信息
     * @param User|int   $user_or_id
     * @param array $data
     * @return ?User
     */
    public function updateUser($user_or_id, array $data) : ?User
    {
        $user = $user_or_id;
        if (is_numeric($user_or_id)) {
            $user = $this->findUserById(intval($user_or_id));
        }
        if ($user) {
            $user->fill($data);
            $this->saveUser($user);
            return $user;
        }
        return null;
    }

    /**
     * 更新用户通信时间
     * @param User|int   $id
     */
    public function updateTime($user_or_id)
    {
        $user = $user_or_id;
        if (is_numeric($user_or_id)) {
            $user = $this->findUserById($user_or_id);
        }
        if ($user) {
            $user->updated_at = time();
            $this->saveUser($user);
        }
    }

    /**
     * 返回用户数量
     */
    public function count()
    {
        return $this->redis->scard($this->user_list_key);
    }
    
    /**
     * 清除用户数据
     */
    public function clear()
    {
        $users = $this->redis->smembers($this->user_list_key);
        foreach($users as $_id) {
            $this->redis->del(sprintf($this->user_info_key, $_id));
        }
        $this->redis->del($this->user_list_key);
    }

    /**
     * 用户准备就绪
     * @param User  $user
     */
    public function ready(User $user) : bool
    {
        if ($user->room <= 0) return false; //用户不在房间内,不能进入就绪状态

        $user->status = GameStatus::USER_READY;
        $this->saveUser($user);
        return true;
    }

    /**
     * 用户离开房间
     * @param User  $user
     */
    public function leave(User $user) : bool
    {
        if ($user->status == USER_PLAYING) return false;    //用户在游戏状态,不能离开房间
        
        $user->status = GameStatus::USER_WAITING;
        $user->room = 0;
        $user->position = 0;
        $user->updated_at = time();
        $this->saveUser($user);
        return true;
    }

    /**
     * 玩家掉线
     * @param User  $user
     */
    public function disconnected(User $user)
    {
        $user->status = GameStatus::USER_DISCONNECTED;
        $this->saveUser($user);
    }
}